#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>  
#include <sys/types.h>  

#define COMMON_LOG(fmt, ...)    printf("%s:%d: " fmt "\n", __FUNCTION__, __LINE__, __VA_ARGS__)
  
#pragma pack(2)  
  
/* 位图文件头*/
typedef struct BITMAPFILEHEADER  
{   
    u_int16_t bfType;   
    u_int32_t bfSize;   
    u_int16_t bfReserved1;   
    u_int16_t bfReserved2;   
    u_int32_t bfOffBits;   
}BITMAPFILEHEADER;   

/* 位图信息*/  
typedef struct BITMAPINFOHEADER  
{   
    u_int32_t biSize;   
    u_int32_t biWidth;   
    u_int32_t biHeight;   
    u_int16_t biPlanes;   
    u_int16_t biBitCount;   
    u_int32_t biCompression;   
    u_int32_t biSizeImage;   
    u_int32_t biXPelsPerMeter;   
    u_int32_t biYPelsPerMeter;   
    u_int32_t biClrUsed;   
    u_int32_t biClrImportant;   
}BITMAPINFODEADER;  

/* 调色板*/  
typedef struct tagPALETTEENTRY {
  u_int8_t peRed; 
  u_int8_t peGreen; 
  u_int8_t peBlue; 
  u_int8_t peFlags; 
} PALETTEENTRY; 

/* 位图文件描述*/
typedef struct tagBITMAP_FILE{

  BITMAPFILEHEADER bitmapheader;
  BITMAPINFODEADER bitmapinfoheader;
  PALETTEENTRY palette[2];
  u_int8_t buffer[0];
} BITMAP_FILE;

////////////////////////////////////////////////////////////////////////////

static long int crv_tab[256];
static long int cbu_tab[256];
static long int cgu_tab[256];
static long int cgv_tab[256];
static long int tab_76309[256];
static unsigned char clp[1024];   //for clip in CCIR601

void init_yuv420p_table()
{
    long int crv,cbu,cgu,cgv;
    int i,ind;

    crv = 104597; cbu = 132201;  /* fra matrise i global.h */
    cgu = 25675;  cgv = 53279;

    for (i = 0; i < 256; i++)
    {
        crv_tab[i] = (i-128) * crv;
        cbu_tab[i] = (i-128) * cbu;
        cgu_tab[i] = (i-128) * cgu;
        cgv_tab[i] = (i-128) * cgv;
        tab_76309[i] = 76309*(i-16);
    }

    for (i = 0; i < 384; i++)
        clp[i] = 0;
    ind = 384;
    for (i = 0;i < 256; i++)
        clp[ind++] = i;
    ind = 640;
    for (i = 0;i < 384; i++)
        clp[ind++] = 255;
}

/**
内存分布
					w
			+--------------------+
			|Y0Y1Y2Y3...         |
			|...                 |   h
			|...                 |
			|                    |
			+--------------------+
			|U0U1      |
			|...       |   h/2
			|...       |
			|          |
			+----------+
			|V0V1      |
			|...       |  h/2
			|...       |
			|          |
			+----------+
				w/2
 */
void yuv420p_to_rgb24(unsigned char* yuvbuffer,unsigned char* rgbbuffer, int width,int height)
{
    int y1, y2, u, v;
    unsigned char *py1, *py2;
    int i, j, c1, c2, c3, c4;
    unsigned char *d1, *d2;
    unsigned char *src_u, *src_v;
    static int init_yuv420p = 0;

    src_u = yuvbuffer + width * height;   // u
    src_v = src_u + width * height / 4;  // v

    py1 = yuvbuffer;   // y
    py2 = py1 + width;
    d1 = rgbbuffer;
    d2 = d1 + 3 * width;

    if (init_yuv420p == 0)
    {
        init_yuv420p_table();
        init_yuv420p = 1;
    }

    for (j = 0; j < height; j += 2)
    {
        for (i = 0; i < width; i += 2)
        {
            u = *src_u++;
            v = *src_v++;

            c1 = crv_tab[v];
            c2 = cgu_tab[u];
            c3 = cgv_tab[v];
            c4 = cbu_tab[u];

            //up-left
            y1 = tab_76309[*py1++];
            *d1++ = clp[384+((y1 + c1)>>16)];
            *d1++ = clp[384+((y1 - c2 - c3)>>16)];
            *d1++ = clp[384+((y1 + c4)>>16)];

            //down-left
            y2 = tab_76309[*py2++];
            *d2++ = clp[384+((y2 + c1)>>16)];
            *d2++ = clp[384+((y2 - c2 - c3)>>16)];
            *d2++ = clp[384+((y2 + c4)>>16)];

            //up-right
            y1 = tab_76309[*py1++];
            *d1++ = clp[384+((y1 + c1)>>16)];
            *d1++ = clp[384+((y1 - c2 - c3)>>16)];
            *d1++ = clp[384+((y1 + c4)>>16)];

            //down-right
            y2 = tab_76309[*py2++];
            *d2++ = clp[384+((y2 + c1)>>16)];
            *d2++ = clp[384+((y2 - c2 - c3)>>16)];
            *d2++ = clp[384+((y2 + c4)>>16)];
        }
        d1  += 3*width;
        d2  += 3*width;
        py1 += width;
        py2 += width;
    }
}

#pragma pack()  

int main( int argc, char **argv)  
{  
    int x;
    int y;
    int index;

    int width = 3200;
    int height = 2000;
    int yuv_len;
    int rgb_len;

    unsigned char *src;
    unsigned char *dst;
    unsigned char *bmp_data;
    char *srcFile = NULL;
    char dstFile[256];
    int i = 0;

    FILE *fp;

    BITMAP_FILE bp_file;

    if(argc <= 1)
    {
        COMMON_LOG("%s", "Please input file name");
        return -1;
    }

    srcFile = argv[1];

    for(i=0; srcFile[i] != '.'; i++)
    {
        dstFile[i] = srcFile[i];
    }
    dstFile[i] = '.';
    dstFile[i+1] = 'b';
    dstFile[i+2] = 'm';
    dstFile[i+3] = 'p';
    dstFile[i+4] = '\0';
    printf("Output file is %s\n", dstFile);

    if(argc > 2)
    {
        if(argv[2][1] == 's')
        {
            width = 1280;
            height = 800;
        }
    }

    yuv_len = width * height * 3 / 2;
    rgb_len = width * height * 3;

    fp = fopen(srcFile, "rb");
    if (NULL == fp)
    {
        COMMON_LOG("%s", "file not exist");
        return -1;
    }

    /* yuv 转换成1bpp的bmp，只需要y分量即可，
            对于4:2:0的yuv图片，只需要前width * height 个字节,
            转换后需要width * height / 8个字节来表示。
            由于例子中的yuv是1080p的，下面未考虑width的4字节对齐
       */

    src = malloc(yuv_len);
    if (NULL == src)
    {
        COMMON_LOG("malloc %d bytes fail.", yuv_len);
        fclose(fp);
        return -1;
    }

    dst = malloc(rgb_len);
    if (NULL == dst)
    {
        COMMON_LOG("malloc %d bytes fail.", rgb_len);
        free(src);
        fclose(fp);
        return -1;
    }

    bmp_data = malloc(rgb_len);
    if (NULL == bmp_data)
    {
        COMMON_LOG("malloc %d bytes fail.", rgb_len);
        free(src);
        free(dst);
        fclose(fp);
        return -1;
    }

    memset(&bp_file, 0, sizeof(bp_file));
    
    bp_file.bitmapheader.bfType = ('B' | ('M' << 8));
    bp_file.bitmapheader.bfSize = sizeof(BITMAP_FILE) + (width * height * 3);
    bp_file.bitmapheader.bfOffBits = sizeof(BITMAP_FILE);

    bp_file.bitmapinfoheader.biSize = sizeof(BITMAPINFODEADER);
    bp_file.bitmapinfoheader.biWidth = width;
    bp_file.bitmapinfoheader.biHeight = height;
    bp_file.bitmapinfoheader.biPlanes = 1;
    bp_file.bitmapinfoheader.biBitCount = 24; //24位真彩
    bp_file.bitmapinfoheader.biCompression = 0;
    bp_file.bitmapinfoheader.biSizeImage = width * height * 3;
    bp_file.bitmapinfoheader.biClrUsed = 0;
    bp_file.bitmapinfoheader.biClrImportant = 0;

    /* 黑*/
    bp_file.palette[0].peRed = 0;
    bp_file.palette[0].peGreen = 0;
    bp_file.palette[0].peBlue = 0;

    /* 白*/
    bp_file.palette[1].peRed = 255;
    bp_file.palette[1].peGreen = 255;
    bp_file.palette[1].peBlue = 255;

    memset(src, 0, yuv_len);
    fread(src, yuv_len, 1, fp);
    fclose(fp);

    memset(dst, 0, rgb_len);
    yuv420p_to_rgb24(src, dst, width, height);
//    fp = fopen("test2.rgb", "wb");
//    fwrite(dst, 1, width * height * 3, fp);
//    fclose(fp);

    index = 0;
    /* 从左下角到右上角扫描*/
    for(y=height - 1; y >= 0; y--) 
    {
        for(x=0; x < (width); x++)
        {  
//            if (src[y*width+x] > 128)
//            {
//                dst[index/8] |= (1 << (7 -(index % 8)));
//            }
            bmp_data[index] = dst[y*width*3+x*3+2];  //B
            bmp_data[index+1] = dst[y*width*3+x*3+1];  //G
            bmp_data[index+2] = dst[y*width*3+x*3];//R
            index=index+3;
        }
    }

    fp = fopen(dstFile, "wb");
    fwrite(&bp_file, sizeof(bp_file), 1, fp);
    fwrite(bmp_data, rgb_len, 1, fp);
    fclose(fp);

    free(bmp_data);
    free(dst);
    free(src);
    return 0;  
}  
